﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using DocumentFormat.OpenXml.Drawing;
using OpenPowerPoint.Collection;
using OpenPowerPoint.Conversion;

namespace OpenPowerPoint.Slides
{
    public class Theme : DependencyObject, INotify
    {
        public enum EColors : byte
        {
            None = 0,

            Light1 = 1,
            Dark1 = 2,
            Light2 = 3,
            Dark2 = 4,

            Accent1 = 5,
            Accent2 = 6,
            Accent3 = 7,
            Accent4 = 8,
            Accent5 = 9,
            Accent6 = 10,

            Hyperlink = 11,
            HyperlinkActive = 12,

            Text1 = 13,
            Background1 = 14,
            Text2 = 15,
            Background2 = 16,

            ThemeColor = 17
        }

        #region Properties
        public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Theme),
            new PropertyMetadata(""));

        /// <summary>
        /// The name of the Theme
        /// </summary>
        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }

        public static readonly DependencyProperty Light1Property = DependencyProperty.Register("Light1", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.White, (byte)EColors.Light1),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Light1))));  //We have to coerce the value to apply the additional infos to it

        /// <summary>
        /// The primary light color (white)
        /// </summary>
        public Color Light1
        {
            get { return (Color)GetValue(Light1Property); }
            set { SetValue(Light1Property, value); }
        }

        public static readonly DependencyProperty Light2Property = DependencyProperty.Register("Light2", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Color.FromRgb(231, 230, 230), (byte)EColors.Light2),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Light2))));

        /// <summary>
        /// The secondary light color
        /// </summary>
        public Color Light2
        {
            get { return (Color)GetValue(Light2Property); }
            set { SetValue(Light2Property, value); }
        }

        public static readonly DependencyProperty Dark1Property = DependencyProperty.Register("Dark1", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Black, (byte)EColors.Dark1),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Dark1))));

        /// <summary>
        /// The primary dark color (black)
        /// </summary>
        public Color Dark1
        {
            get { return (Color)GetValue(Dark1Property); }
            set { SetValue(Dark1Property, value); }
        }

        public static readonly DependencyProperty Dark2Property = DependencyProperty.Register("Dark2", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Color.FromRgb(68, 84, 106), (byte)EColors.Dark2),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Dark2))));

        /// <summary>
        /// The secondary dark color
        /// </summary>
        public Color Dark2
        {
            get { return (Color)GetValue(Dark2Property); }
            set { SetValue(Dark2Property, value); }
        }

        public static readonly DependencyProperty Accent1Property = DependencyProperty.Register("Accent1", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Black, (byte)EColors.Accent1),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Accent1))));

        /// <summary>
        /// The first custom color
        /// </summary>
        public Color Accent1
        {
            get { return (Color)GetValue(Accent1Property); }
            set { SetValue(Accent1Property, value); }
        }

        public static readonly DependencyProperty Accent2Property = DependencyProperty.Register("Accent2", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Black, (byte)EColors.Accent2),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Accent2))));

        /// <summary>
        /// The second custom color
        /// </summary>
        public Color Accent2
        {
            get { return (Color)GetValue(Accent2Property); }
            set { SetValue(Accent2Property, value); }
        }

        public static readonly DependencyProperty Accent3Property = DependencyProperty.Register("Accent3", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Black, (byte)EColors.Accent3),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Accent3))));

        /// <summary>
        /// The third custom color
        /// </summary>
        public Color Accent3
        {
            get { return (Color)GetValue(Accent3Property); }
            set { SetValue(Accent3Property, value); }
        }

        public static readonly DependencyProperty Accent4Property = DependencyProperty.Register("Accent4", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Black, (byte)EColors.Accent4),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Accent4))));

        /// <summary>
        /// The fourth custom color
        /// </summary>
        public Color Accent4
        {
            get { return (Color)GetValue(Accent4Property); }
            set { SetValue(Accent4Property, value); }
        }

        public static readonly DependencyProperty Accent5Property = DependencyProperty.Register("Accent5", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Black, (byte)EColors.Accent5),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Accent5))));

        /// <summary>
        /// The fifth custom color
        /// </summary>
        public Color Accent5
        {
            get { return (Color)GetValue(Accent5Property); }
            set { SetValue(Accent5Property, value); }
        }

        public static readonly DependencyProperty Accent6Property = DependencyProperty.Register("Accent6", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Black, (byte)EColors.Accent6),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Accent6))));

        /// <summary>
        /// The sixth custom color
        /// </summary>
        public Color Accent6
        {
            get { return (Color)GetValue(Accent6Property); }
            set { SetValue(Accent6Property, value); }
        }

        public static readonly DependencyProperty HyperlinkProperty = DependencyProperty.Register("Hyperlink", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Blue, (byte)EColors.Hyperlink),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.Hyperlink))));

        /// <summary>
        /// The default color for hyperlinks
        /// </summary>
        public Color Hyperlink
        {
            get { return (Color)GetValue(HyperlinkProperty); }
            set { SetValue(HyperlinkProperty, value); }
        }

        public static readonly DependencyProperty HyperlinkActiveProperty = DependencyProperty.Register("HyperlinkActive", typeof(Color), typeof(Theme),
            new PropertyMetadata(UnitConversion.ExToColor(Colors.Violet, (byte)EColors.HyperlinkActive),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => UnitConversion.ExToColor((Color)e, (byte)EColors.HyperlinkActive))));

        /// <summary>
        /// The color for clicked hyperlinks
        /// </summary>
        public Color HyperlinkActive
        {
            get { return (Color)GetValue(HyperlinkActiveProperty); }
            set { SetValue(HyperlinkActiveProperty, value); }
        }

        public static readonly DependencyProperty MajorLatinFontProperty = DependencyProperty.Register("MajorLatinFont", typeof(FontFamily), typeof(Theme),
            new PropertyMetadata(new FontFamily("Times New Roman\0mjlt"),   //mjlt = MaJorLaTin
            (o, e) => ((Theme)o).OnPropertyChanged(e),
            new CoerceValueCallback((o, e) => new FontFamily(((FontFamily)e).Source + "\0mjlt"))));

        public FontFamily MajorLatinFont
        {
            get { return (FontFamily)GetValue(MajorLatinFontProperty); }
            set { SetValue(MajorLatinFontProperty, value); }
        }

        public static readonly DependencyProperty MinorLatinFontProperty = DependencyProperty.Register("MinorLatinFont", typeof(FontFamily), typeof(Theme),
            new PropertyMetadata(new FontFamily("Arial\0mnlt"),   //mnlt = MiNorLaTin
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => new FontFamily(((FontFamily)e).Source + "\0mnlt"))));

        public FontFamily MinorLatinFont
        {
            get { return (FontFamily)GetValue(MinorLatinFontProperty); }
            set { SetValue(MinorLatinFontProperty, value); }
        }

        public static readonly DependencyProperty MajorAsianFontProperty = DependencyProperty.Register("MajorAsianFont", typeof(FontFamily), typeof(Theme),
            new PropertyMetadata(new FontFamily("\0mjas"),
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => new FontFamily(((FontFamily)e).Source + "\0mjas"))));    //mjas = MaJorASian

        public FontFamily MajorAsianFont
        {
            get { return (FontFamily)GetValue(MajorAsianFontProperty); }
            set { SetValue(MajorAsianFontProperty, value); }
        }

        public static readonly DependencyProperty MinorAsianFontProperty = DependencyProperty.Register("MinorAsianFont", typeof(FontFamily), typeof(Theme),
            new PropertyMetadata(new FontFamily("\0mnas"),    //mnas = MiNorASian
                (o, e) => ((Theme)o).OnPropertyChanged(e),
                new CoerceValueCallback((o, e) => new FontFamily(((FontFamily)e).Source + "\0mnas"))));

        public FontFamily MinorAsianFont
        {
            get { return (FontFamily)GetValue(MinorAsianFontProperty); }
            set { SetValue(MinorAsianFontProperty, value); }
        }
        #endregion

        #region Load/Save
        public static Theme Load(DocumentFormat.OpenXml.Drawing.Theme theme)
        {
            Theme ret = new Theme();

            ret.Name = theme.Name;

            GetColorDelegate dark1 = GetColor;
            GetColorDelegate dark2 = GetColor;
            GetColorDelegate light1 = GetColor;
            GetColorDelegate light2 = GetColor;
            GetColorDelegate accent1 = GetColor;
            GetColorDelegate accent2 = GetColor;
            GetColorDelegate accent3 = GetColor;
            GetColorDelegate accent4 = GetColor;
            GetColorDelegate accent5 = GetColor;
            GetColorDelegate accent6 = GetColor;

            IAsyncResult d1 = dark1.BeginInvoke(theme.ThemeElements.ColorScheme.Dark1Color, null, null);
            IAsyncResult d2 = dark2.BeginInvoke(theme.ThemeElements.ColorScheme.Dark2Color, null, null);
            IAsyncResult l1 = light1.BeginInvoke(theme.ThemeElements.ColorScheme.Light1Color, null, null);
            IAsyncResult l2 = light2.BeginInvoke(theme.ThemeElements.ColorScheme.Light2Color, null, null);
            IAsyncResult a1 = accent1.BeginInvoke(theme.ThemeElements.ColorScheme.Accent1Color, null, null);
            IAsyncResult a2 = accent2.BeginInvoke(theme.ThemeElements.ColorScheme.Accent2Color, null, null);
            IAsyncResult a3 = accent3.BeginInvoke(theme.ThemeElements.ColorScheme.Accent3Color, null, null);
            IAsyncResult a4 = accent4.BeginInvoke(theme.ThemeElements.ColorScheme.Accent4Color, null, null);
            IAsyncResult a5 = accent5.BeginInvoke(theme.ThemeElements.ColorScheme.Accent5Color, null, null);
            IAsyncResult a6 = accent6.BeginInvoke(theme.ThemeElements.ColorScheme.Accent6Color, null, null);

            if (theme.ThemeElements.FontScheme.MajorFont.LatinFont != null)
                ret.MajorLatinFont = new FontFamily(theme.ThemeElements.FontScheme.MajorFont.LatinFont.Typeface.Value);
            if (theme.ThemeElements.FontScheme.MinorFont.LatinFont != null)
                ret.MinorLatinFont = new FontFamily(theme.ThemeElements.FontScheme.MinorFont.LatinFont.Typeface.Value);
            if (theme.ThemeElements.FontScheme.MajorFont.EastAsianFont != null)
                ret.MajorAsianFont = new FontFamily(theme.ThemeElements.FontScheme.MajorFont.EastAsianFont.Typeface.Value);
            if (theme.ThemeElements.FontScheme.MinorFont.EastAsianFont!= null)
                ret.MinorAsianFont = new FontFamily(theme.ThemeElements.FontScheme.MinorFont.EastAsianFont.Typeface.Value);
    
            ret.Dark1 = dark1.EndInvoke(d1);
            ret.Dark2 = dark2.EndInvoke(d2);
            ret.Light1 = light1.EndInvoke(l1);
            ret.Light2 = light2.EndInvoke(l2);
            ret.Accent1 = accent1.EndInvoke(a1);
            ret.Accent2 = accent2.EndInvoke(a2);
            ret.Accent3 = accent3.EndInvoke(a3);
            ret.Accent4 = accent4.EndInvoke(a4);
            ret.Accent5 = accent5.EndInvoke(a5);
            ret.Accent6 = accent6.EndInvoke(a6);

            return ret;
        }

        private delegate Color GetColorDelegate(Color2Type baseColor);

        private static Color GetColor(Color2Type baseColor)
        {
            if (baseColor.SystemColor != null)
            {
                return (Color)ColorConverter.ConvertFromString("#" + baseColor.SystemColor.LastColor);
            }
            else if (baseColor.RgbColorModelHex != null)
            {
                return (Color)ColorConverter.ConvertFromString("#" + baseColor.RgbColorModelHex.Val);
            }

            return Colors.White;
        }
        #endregion

        #region Functions
        /// <summary>
        /// Updates a color based on its additional data
        /// </summary>
        /// <param name="input">The color to parse</param>
        /// <returns>The updated color</returns>
        public Color UpdateColor(Color input)
        {
            var data = (EColors)Conversion.UnitConversion.ColorToEx(input);

            switch (data)
            {
                case EColors.Accent1:
                    return Accent1;
                case EColors.Accent2:
                    return Accent2;
                case EColors.Accent3:
                    return Accent3;
                case EColors.Accent4:
                    return Accent4;
                case EColors.Accent5:
                    return Accent5;
                case EColors.Accent6:
                    return Accent6;
                case EColors.Light1:
                    return Light1;
                case EColors.Light2:
                    return Light2;
                case EColors.Dark1:
                    return Dark1;
                case EColors.Dark2:
                    return Dark2;
                case EColors.Hyperlink:
                    return Hyperlink;
                case EColors.HyperlinkActive:
                    return HyperlinkActive;
                default:
                    return input;
            }
        }
        #endregion

        #region INotify
        bool _handleChanged = true;
        private void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (_handleChanged == false)
                return;

            _handleChanged = false;
            SProperty property = new SProperty(e.Property, e.OldValue, e.NewValue);
            var handler = PropertyChanged;
            if (handler != null) handler(this, property);

            SetValue(e.Property, property.NewValue);

            _handleChanged = true;
        }

        internal event NotifyableList<INotify>.PropertyChangedDelegate PropertyChanged;

        event NotifyableList<INotify>.PropertyChangedDelegate INotify.PropertyChanged
        {
            add { PropertyChanged += value; }
            remove { PropertyChanged -= value; }
        }
        #endregion
    }
}
